module Main exposing (..)

import Browser
import Browser.Navigation as Nav
import Html exposing (Html)
import Html.Attributes exposing (src)
import Url exposing (..)
import Element exposing (..)
import Element.Border as Border
import Element.Font as Font
import Html.Attributes exposing (src)
import Element.Input as Input
import Element.Background as Background
import LineChart
import Svg exposing (Svg)
import StoryNavWidget exposing (WidgetData, Datum, navWidget)
import Either exposing (Either)
import Vocabularies exposing (Relation)
import Tree as T
import TreeView as TV


---- MODEL ----

scaled = modular 16 1.25

defaults =
    { itemImage = "http://localhost:3000/logo.svg"
    , profilePicture = "/logo.svg"
    , itemDescription = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
    }

testUser =
    { id = "asdf"
    , userName = "John Doe"
    , balance = 100
    , profilePicture = Nothing
    }

-- Display model : BEGIN

type alias NodeData =
    { uid : String
    , label : String
    }

type alias Item =
    { id : String 
    , title : String
    , url : Maybe String
    , description : Maybe String
    , relations : List Relation
    }

mockItem =
    { id = "mock"
    , title = "mock"
    , url = Nothing
    , description = Just "Some mock description"
    , relations = []
    }

generateItems : Item -> List Int -> List Item
generateItems item list =
    List.map 
        (\n -> { item | 
                 id = String.fromInt n
               , title = "Title - " ++ String.fromInt n
               , url = Just ("/itemphotos/" ++ String.fromInt n ++ ".jpg")
               }
        )
        list

type alias Collection = 
    { id : String
    , title : String
    , description : String
    , collection : List Item
    }

-- Display model : END

-- Organizational Model : BEGIN

type alias Repository = 
    { id : String 
    , title : String
    , description : String
    , groups : List Group
    }


--TODO: Find a way to eliminate repeated code.
type Group = RecordGroup { id : String
                         , title : String
                         , person : Person
                         , subGroups : Maybe (List Group)
                         , series : Maybe (List Series)
                         }
           | ManuscriptGroup { id : String
                             , title : String
                             , person : Person
                             , subGroups : Maybe (List Group)
                             , series : Maybe (List Series)
                             }

type Person = NaturalPerson
            | LegalPerson

type Series = Series { id : String
                     , title : String
                     , description : String
                     , subSeries : Maybe (List Series)
                     , files : Maybe (List File)
                     }
            | SubSeries { id : String
                        , title : String
                        , description : String
                        , parentSeries : Series
                        , files : Maybe (List File)
                        }

type alias File = 
    { id : String 
    , title : String
    , description : String
    , items : Maybe (List Item)
    }

-- Organizational Model : END


type alias Model =
    { rootNodes : List (T.Node NodeData)
    , treeModel : TV.Model NodeData String Never ()
    , selectedNode : Maybe NodeData
    , url : Url.Url
    , key : Nav.Key
    , user : UserType
    , query : { query : String }
    , storyNavWidgetData : WidgetData
    , hinted : Maybe Datum
    , collectionList : List (Either Item Collection)
    , items : List (Item)
    , header : { user : UserType }
    }

nodeLabel : T.Node NodeData -> String
nodeLabel n =
    case n of
        T.Node node -> node.data.label

nodeUid : T.Node NodeData -> TV.NodeUid String
nodeUid n =
    case n of
        T.Node node -> TV.NodeUid node.data.uid

configuration : TV.Configuration NodeData String
configuration =
    TV.Configuration nodeUid nodeLabel TV.defaultCssClasses

initWidgetData : WidgetData
initWidgetData =
    { one = [{xAxis = 1.0, yAxis = 1.0}]
    , two = [{ xAxis = 1.0, yAxis = 2.0}]
    , three = [{ xAxis = 1.0, yAxis = 3.0}]
    }

initialModel : Url.Url -> Nav.Key -> Model
initialModel url key =
    let
        rootNodes =
            [ T.Node
              { children =
                [ T.Node 
                    { children = 
                        [ T.Node 
                            { children =[]
                            , data = NodeData "file" "Correspondence, writings, and other material"
                            }
                        , T.Node 
                            { children =[]
                            , data = NodeData "file" "American Expeditionary Forces (A.E.F.)"
                            }
                        , T.Node 
                            { children =[]
                            , data = NodeData "file" "General Edmund Allenby"
                            }
                        ]
                    , data = NodeData "Series" "Textual materials" 
                    }
                , T.Node
                    { children = []
                    , data = NodeData "series" "Graphic materials"
                    }
                ]
              , data = NodeData "collection" "Lowell Thomas Paper"
              }
            ]
    in
    { rootNodes = rootNodes
    , treeModel = TV.initializeModel configuration rootNodes
    , selectedNode = Nothing
    , url = url 
    , key = key
    , user = LoggedInUser testUser
    , query = { query = "" }
    , storyNavWidgetData = initWidgetData
    , hinted = Nothing
    , collectionList = []
    , items = generateItems  mockItem (List.range 1 10)
    , header = { user = LoggedInUser testUser }
    }

setWidgetData : WidgetData -> Model -> Model
setWidgetData widgetData model =
    { model | storyNavWidgetData = widgetData }


setHint : Maybe Datum -> Model -> Model
setHint hinted model =
    { model | hinted = hinted }

addCmd : Cmd Msg -> Model -> ( Model, Cmd Msg )
addCmd cmd model =
    ( model, Cmd.none )

init : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg )
init flags url key =
    ( initialModel url key
    , Cmd.none 
    )

type alias User = 
    { id : String
    , userName : String
    , balance : Int
    , profilePicture : Maybe String
    }

type UserType = LoggedInUser User
    | AnonymousUser


---- UPDATE ----


type Msg
    = NoOp
    | UrlChanged Url.Url
    | LinkClicked Browser.UrlRequest
    | Search String
    | RecieveWidgetData WidgetData
    | Hint (Maybe Datum)
    | TreeViewMsg (TV.Msg String)
    | ExpandAll
    | CollapseAll


update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        RecieveWidgetData numbers ->
            model
                |> setWidgetData numbers
                |> addCmd Cmd.none
        Hint point ->
            model
                |> setHint point
                |> addCmd Cmd.none
        TreeViewMsg tvMsg ->
            ({ model | treeModel = TV.update tvMsg model.treeModel }, Cmd.none)
        ExpandAll ->
            ({ model | treeModel = TV.expandAll model.treeModel }, Cmd.none)
        CollapseAll ->
            ({ model | treeModel = TV.collapseAll model.treeModel }, Cmd.none)
        _ ->
            ( model, Cmd.none )



---- VIEW ----


view : Model -> Browser.Document Msg
view model =
    { title = "Anansi: Storytelling for change and activism!"
    , body = page model
    }

page : Model -> List (Html Msg)
page model =
    [ layout 
        [ Font.family  
            [ Font.typeface "Gotham Rounded A"
            , Font.sansSerif
            ]
        , Font.color <| rgb255 10 10 10
        ] <|
        column 
            [ height fill
            , width fill
            , spacing 20
            ]
            [ header model.header
            --, searchBar model.query
            --, contentWrapper model
            , fashionMagLayout model
            -- , Element.html <| (Html.map htmlMap (navWidget model.storyNavWidgetData model.hinted))
            , footer
            ]
    ]

htmlMap : StoryNavWidget.Msg -> Msg
htmlMap navWidgetMsg =
    case navWidgetMsg of
        StoryNavWidget.Hint x ->
            Hint x
    
        StoryNavWidget.RecieveWidgetData y ->
            RecieveWidgetData y


header : { user : UserType } -> Element Msg
header { user } =
    wrappedRow 
    [ width fill 
    , height <| fillPortion 2
    , Border.color <| rgb255 100 10 10
    , Border.solid 
    , Border.glow (rgb255 100 100 100) 1
    ]
    [ el [ alignLeft
         , padding 30
         ] 
         (image [ height <| px 120 ] { description = "Anansi"
                                    , src ="/logo.png" 
                                    }
         )
    , link
        [ Font.size (round <| scaled 3)
        , Font.extraBold
        , Font.color (rgb255 73 73 73)
        , padding 50
        ]
        { url = "#"
        , label = (text "Anansi")
        }
    , link
        [ Font.hairline
        , padding 50
        ]
        { url = "#"
        , label = (text "STORIES")
        }
    , link 
        [ Font.hairline
        , padding 50
        ]
        { url = "#"
        , label = (text "ABOUT")}
    , row [ alignRight
          , spacing 10
          , padding 30
          ]
          <| userHeaderMenu user
    ]

userHeaderMenu : UserType -> List ( Element Msg )
userHeaderMenu user =
    case user of
        AnonymousUser ->
            [ buttonMaker "Register" Nothing
            , buttonMaker "Login" Nothing
            ]
    
        LoggedInUser u ->
            [ column []
                [ ( image [ height <| px 50
                              , Border.rounded 360
                              ]
                              { description = "User's image"
                              , src = case u.profilePicture of
                                  Just a ->
                                      a
                              
                                  Nothing ->
                                      defaults.profilePicture
                              }
                      )
                ]
            , column 
                [ Font.hairline
                , Font.size (round <| scaled 1)
                ]
                [ ( text u.userName )
                , el [] (text "Curator")
                ]
            ]

buttonMaker : 
    String 
    -> Maybe msg 
    -> Element msg
buttonMaker label msg =
    el [ padding 5
       , spacing 2
       ]
       ( Input.button [ Background.color <| rgb255 64 108 213 
                       , padding 10
                      ]
                      { label = text label
                      , onPress = msg
                      }
         )


searchBar : { query : String } -> Element Msg
searchBar { query } =
    column
        [ padding 10
        , width <| px 500
        , centerX
        ]
        [ searchInputBox query ]

searchInputBox : String -> Element Msg
searchInputBox q =
    Input.search 
        [ width <| px 500 ]
        { onChange = Search
        , text = q
        , placeholder = Just (Input.placeholder [] (text "Search here ..."))
        , label = Input.labelHidden "Search"
        }

fashionMagLayout : Model -> Element Msg
fashionMagLayout model =
    wrappedRow
        [ width fill ]
        [ informationPane  model
        , recordsList model
        ]

--TODO: Convert to { ead : EAD }
informationPane : Model -> Element Msg
informationPane model =
    column
        [ width <| fillPortion 4
        , alignTop
        ]
        [ paragraph 
            [ spacing (round <| scaled -1) 
            , padding 25
            ]
            [ el 
                [ Font.size (round <| scaled 10) 
                , Font.letterSpacing -16
                , Font.heavy
                , Font.family
                    [ Font.typeface "Helvetica"
                    ]
                , Border.solid
                , Border.color (rgb 0 0.7 0)
                ]
                (text <| String.toUpper "Lowell Thomas Papers")
            ]
        , paragraph 
            [ padding 20 ]
            [ el 
                [ Font.hairline 
                , Font.size (round <| scaled 1)
                -- , Font.italic
                ] 
                (text "The Lowell Thomas Papers encompasses a wide range of materials documenting the life, family, and professional career of Lowell Thomas. These materials include correspondence, administrative and financial documents, manuscripts, printed materials, photographic materials, motion picture films, audio recordings, and memorabilia.")
            ]
        , paragraph
            [ paddingXY 85 20 
            , Font.color <| (rgb255 73 73 73) 
            , Font.size (round <| scaled -1)
            ]
            [ el 
                [ padding 15
                ]
                ( text <| (String.fromChar <| Char.fromCode 0x258C) ++ "Biography & History ")
            , el
                [ Font.hairline 
                ]
                (text "Lowell Jackson Thomas was born in Woodington, Ohio on 6 April 1892 to two school teachers, Harry G. Thomas and Harriet Wagner Thomas. The family did not remain in Darke County, Ohio for very long because Lowell's father wanted to become a doctor. Harry G. Thomas's pursuit of a medical degree led the family to Kirkman, Iowa where five-year-old Lowell was enrolled in school for the first time. Harry's studies soon led him to the University of Nebraska. Upon graduation, Harry G. Thomas followed his brother's suggestion to begin his medical practice in the twin mining boom towns of Cripple Creek and Victor, Colorado. It was here in the rugged American West that Lowell spent the better part of his childhood and young adult years.")
            , el 
                [ Font.color <| (rgb255 73 73 73) 
                , padding 15
                ]
                ( text <| (String.fromChar <| Char.fromCode 0x258C) ++ "Access Restriction ")
            , el
                [ Font.hairline 
                ]
                (text "Items in this collection do not circulate and may be used in-house only.")
            , el 
                [ Font.color <| (rgb255 73 73 73) 
                , padding 15
                ]
                ( text <| (String.fromChar <| Char.fromCode 0x258C) ++ "Extent ")
            , el [ Font.bold ] (text "73 ")
            , text ("folders, ")
            , el [ Font.bold ] (text "380 ")
            , text ("records.")
            ]
        , collectionNavigation
        ]

collectionNavigation : Element Msg
collectionNavigation =
    column
        []
        [ el 
            [ Font.color <| (rgb255 73 73 73) 
            , paddingXY 25 20
            , Font.hairline
            ]
            (text "In this collection:")
        , paragraph 
                [ Font.color <| (rgb255 73 73 73) 
                , paddingXY 40 10
                , Font.size (round <| scaled 1)
                ]
                [ paragraph 
                    []
                    seriesList
                ]
        ]

seriesList : List (Element Msg)
seriesList =
    [ ( text <| (String.fromChar <| Char.fromCode 0x258C) ++ "Textual materials ") ]
    ++ fileList1
    ++ [( text <| (String.fromChar <| Char.fromCode 0x258C) ++ "Graphic materials ")]
    -- ++ fileList2

fileList1 : List (Element Msg)
fileList1 =
    [ el [ Font.hairline ] (text "[ Correspondence, writings, and other material ")
    , el [ Font.hairline ] (text "| American Expeditionary Forces (A.E.F.) ")
    , el [ Font.hairline ] (text " | General Edmund Allenby] ")
    ]

fileList2 : List (Element Msg)
fileList2 =
    [ el [ Font.hairline ] (text "[ Correspondence, writings, and other material ")
    , el [ Font.hairline ] (text "| American Expeditionary Forces (A.E.F.) ]")
    ]

recordsList : Model -> Element Msg
recordsList model = column
                        [ width <| fillPortion 5 ]
                        [ row
                            [ Font.hairline
                            , Font.italic
                            , Font.size (round <| scaled 3)
                            , Font.color <| (rgb255 73 73 73)
                            , width fill
                            ]
                            [(text <| (String.fromChar <| Char.fromCode 0x258C) ++ " Records (380) " )]
                        , el 
                            [ padding 25
                            ] <| searchInputBox "Search this collection"
                        , paragraph [] <| List.map itemTile model.items -- [(itemsList model)]
                        ]

contentWrapper : Model -> Element Msg
contentWrapper model =
    row
        [ Font.hairline 
        , Font.size (round <| scaled -1)
        , centerX
        , width fill
        ]
        [ eadNavigation model
        , content model
        ]

eadNavigation : Model -> Element Msg
eadNavigation model =
    column
    [ width <| fillPortion 1
    , height fill
    , Font.hairline 
    , Font.size (round <| scaled 1)
    , paddingXY 15 1
    ]
    [ wrappedRow []--[ width (fill |> maximum 150) ] 
        [ (map TreeViewMsg (TV.view model.treeModel |> html) ) ]
    ]

content : Model -> Element Msg
content model =
    column
    [ width <| fillPortion 4 
    , paddingXY 40 0 ]
    [ breadcrumbs
    , infoPane model
    -- , itemDetailsRow 
    ]

breadcrumbs : Element Msg
breadcrumbs =
    wrappedRow [] [( text "Collection >> Lowell Thomas Papers" )]

infoPane : Model -> Element Msg
infoPane model =
    column
    []
    [ collectionInfo model
    , itemsList model
    ]

collectionInfo : Model -> Element Msg
collectionInfo model =
    column
    [ width fill 
    , padding 15
    ]
    [ el
        [ Font.hairline
        , Font.italic
        , Font.size (round <| scaled 3)
        , Font.color <| (rgb255 73 73 73)
        ]
        ( text <| (String.fromChar <| Char.fromCode 0x258C) ++ " Information"  )
    , paragraph --Use paragraph for all "mixed" text
        [ paddingXY 40 15 
        , Font.color <| (rgb255 73 73 73)
        , Font.alignLeft
        ]
        [ el 
            [ Font.extraBold 
            , Font.size (round <| scaled 3)
            , Font.italic
            ] 
            (text "T")
        , (text "he Lowell Thomas Papers encompasses a wide range of materials documenting the life, family, and professional career of Lowell Thomas. These materials include correspondence, administrative and financial documents, manuscripts, printed materials, photographic materials, motion picture films, audio recordings, and memorabilia." )
        ]
    , column
        [ Font.hairline
        , Font.size (round <| scaled 2)
        , Font.italic
        , paddingXY 50 15
        , Font.color <| (rgb255 73 73 73)
        ]
        [ ( text <| (String.fromChar <| Char.fromCode 0x258C) ++ "Extent")
        , paragraph
            [ Font.size (round <| scaled -1)
            , Font.color <| (rgb255 75 75 75)
            , paddingXY 40 15
            ]
            [ el [ Font.bold ] (text "73 ")
            , text ("folders, ")
            , el [ Font.bold ] (text "380 ")
            , text ("records.")
            ]
        ]
    ]

itemsList : Model -> Element Msg
itemsList model =
    column
    [ width fill 
    , padding 5
    ]
    [ paragraph 
        [ width fill
        -- , padding 5
        -- , spacing 2
        -- , scrollbarY
        -- , centerX
        ] 
        <| List.map itemTile model.items
    ]

-- fashionMagLayoutItemTile : Item -> Element Msg
-- fashionMagLayoutItemTile item =
--     el [ Background. ]
--         column []
--             [ el 
--                 [ ]
--                 ( text item.title )
--             ]

itemTile : Item -> Element Msg
itemTile item =
    column
        [ Background.color <| rgba255 138 147 142 0.33
        , padding 1
        , Font.family 
            [ Font.typeface "Helvetica"
            , Font.sansSerif
            ]
        ]
        [ image 
            [ --width (fill |> minimum 280)
            -- , height (fill |> minimum 100)
            ]
            { src = Maybe.withDefault defaults.itemImage item.url
            , description = Maybe.withDefault "No description available" item.description 
            }
        , paragraph 
            [ Font.bold
            , Font.size (round <| scaled 1)
            -- , width (fill |> minimum 150)
            , padding 10
            ]
            [ (text item.title) ]
        , image 
            [ alignBottom
            , width <| px 30
            , height <| px 30
            , centerX
            ]
            { src = "baseline-expand_more-24px.svg"
            , description = "Click to expand."
            }
        ]

itemContainer : List Item -> Element Msg
itemContainer itemsL =
    wrappedRow 
    [ width fill
    , padding 30
    , spacing 2
    , scrollbarY
    , centerX
    ] 
    <| List.map itemTile itemsL


itemDetailsRow : Element Msg
itemDetailsRow =
    column
        []
        [ el
            [ padding 10 ]
            ( image
                [ height (fill |> minimum 300) ]
                { src = "/logo.svg"
                , description = "Some archival item."
                }
            )
        , column
            [ alignLeft ]
            [ (text "Description: Hello my name is human!")
            ]
        ]

storyNavWidget : Element Msg
storyNavWidget =
    el
        [ height <| px 300
        , width fill
        , Background.uncropped "/story-nav.svg"
        ]
        Element.none

footerColumnAttribs : Int -> List (Element.Attribute Msg)
footerColumnAttribs leftColumnWidth =
    [ width <| fillPortion 1
    , height fill
    , padding 10
    , Font.variant Font.smallCaps
    , Font.size (round <| scaled 1)
    ]

footer : Element Msg
footer =
    column
    [ width fill 
    , height <| fillPortion 2
    , spacing 100
    , padding 50
    , Background.color <| (rgb255 34 34 34)
    , Font.color <| (rgb255 255 255 255)
    , Font.italic
    ]
    [ wrappedRow 
        [ width fill
        ]
        [ column (footerColumnAttribs 1)
            [ (text "WHY")
            , paragraph 
                [ centerX
                , Font.size (round <| scaled -1) 
                , Font.hairline
                , padding 20
                , Font.alignLeft
                ]
                [ (text "There is a need for platforms for effective story telling. Anansi is an effort in that direction.")]
                 ]
        , column (footerColumnAttribs 1)
            [ (text "Support Us")
            , paragraph
                [ centerX
                , Font.size (round <| scaled -1)
                , Font.hairline
                , padding 20
                , Font.alignLeft
                ]
                [ (text "Please support us on Patreon or contact us if you have ideas on how to spread the word about Anansi or QAMRA!")]
            ]
        , column (footerColumnAttribs 0)
            [ (text "Contact")]
        ]
    , el  
        [ centerX
        , Font.size (round <| scaled -1)
        ] 
        (text " ~ Made with love using Elm and Scala ~") ]

---- PROGRAM ----


main : Program () Model Msg
main =
    Browser.application
        { view = view
        , init = init
        , update = update
        , subscriptions = always Sub.none
        , onUrlChange = UrlChanged
        , onUrlRequest = LinkClicked
        }

subscriptions : Model -> Sub Msg
subscriptions model =
    Sub.map TreeViewMsg (TV.subscriptions model.treeModel)

-- CHART TRYOUT TODO: Include in another module
type alias Point =
    { x : Float
    , y : Float
    }

chart : Element Msg
chart =
    Element.html 
        <| LineChart.view1 .x .y
               [ Point 0 2, Point 5 5, Point 5 10 ]